package com.transfer.http.download.MultiThread.task;

import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.transfer.Scheduler.TransferScheduler;
import com.transfer.common.TaskState;
import com.transfer.common.TaskStateResult;
import com.transfer.common.VerifyMD5Task;
import com.transfer.conf.FileTransferConfiguration;
import com.transfer.http.download.taskInterface.FileDownloadTask;
import com.transfer.http.download.taskInterface.FileSliceDownloadTask;
import com.transfer.http.download.taskInterface.TaskControlInterface;

public class FileDownloadMultiTask extends FileDownloadTask implements TaskControlInterface {
  private static final Logger log = LoggerFactory.getLogger(FileDownloadMultiTask.class);
  private int numThread = 0;// 文件下载任务的线程数
  private long fileLengthPerThread = 0;// 文件下载任务的每个线程需要下载的文件长度
  private FileSliceDownloadTask[] fileSliceDownloadTask;
  private FileMergeTask fileMergeTask;
  private VerifyMD5Task verifyMD5Task;
  private long mergeFlushSize;// 合并文件时flush到磁盘的size间隔
  private TaskStateResult taskStateResult;// 任务状态结果：（任务状态，百分比）
  private TaskState taskState = TaskState.WAITING;// 任务状态
  public FileDownloadMultiTask(long taskId, String urlStr, String fileStorageDir, int numThread,
      String MD5) {
    super(taskId, urlStr, fileStorageDir, MD5);
    this.numThread = numThread;
  }

  public FileDownloadMultiTask(long taskId, String urlStr, String fileStorageDir, String MD5) {
    super(taskId, urlStr, fileStorageDir, MD5);
    this.numThread = FileTransferConfiguration.defaultNumThread;
  }

  public FileDownloadMultiTask(long taskId, String urlStr, int numThread, String MD5) {
    super(taskId, urlStr, FileTransferConfiguration.defaultFileStorageDir, MD5);
    this.numThread = numThread;
  }

  public void work() {
    boolean success = doTask();
    if (success) {
      this.taskState = TaskState.SUCCESS;
      log.info("taskId： " + taskId + " 传输文件成功！");
    } else {
      this.taskState = TaskState.FAILURE;
      log.info("taskId： " + taskId + " 传输文件失败！");
    }
  }

  public boolean doTask() {
    this.taskState = TaskState.PREPARING;
    boolean initSuccess = init();
    if (!initSuccess)
      return false;
    CountDownLatch latch = new CountDownLatch(numThread);
    boolean sliceAndSumitTaskSuccess = sliceAndSumitTask(latch);
    if (!sliceAndSumitTaskSuccess)
      return false;
    this.taskState = TaskState.PROGRESSING;
    boolean mergeSuccess=mergeFile(latch);
    if(!mergeSuccess)
      return false;
    boolean verifySuccess=verifyMD5();
    if(!verifySuccess)
      return false;
    return true;
  }

  public boolean init() {
    this.taskStateResult = new TaskStateResult(this.taskState, "%"+this.computeProgress());
    initFileName(urlStr);
    try {
      this.downloadURL = new URL(urlStr);
    } catch (MalformedURLException e) {
      log.info("创建URL对象失败： " + e);
      return false;
    }
    this.fileSliceDownloadTask = new FileSliceDownloadTask[numThread];
    this.mergeFlushSize = FileTransferConfiguration.mergeFlushSize;
    return true;
  }

  private void initFileName(String urlStr) {
    this.fileName =
        urlStr.substring(urlStr.lastIndexOf("/") + 1,
            urlStr.lastIndexOf("?") > 0 ? urlStr.lastIndexOf("?") : urlStr.length());
    if ("".equalsIgnoreCase(this.fileName)) {
      this.fileName = UUID.randomUUID().toString();
    }
  }

  public boolean sliceAndSumitTask(CountDownLatch latch) {
    statusError = false;
    long contentLength = getContentLength();
    if (contentLength == 0)
      return false;
    this.fileLength = contentLength;
    this.fileLengthPerThread = contentLength / numThread;
    long[] startPos = getBreakpointPerThread();
    long endPos;
    for (int i = 0; i < numThread; i++) {
      startPos[i] += fileLengthPerThread * i;
      if (i == numThread - 1) {
        endPos = contentLength;
      } else {
        endPos = fileLengthPerThread * (i + 1) - 1;
      }
      FileSliceDownloadTask runnableTask =
          new FileSliceDownloadTask(this, latch, i, startPos[i], endPos);
      fileSliceDownloadTask[i] = runnableTask;
      TransferScheduler.getFixedExecutor().submit(runnableTask);
    }
    return true;
  }

  private long getContentLength() {
    HttpURLConnection con;
    try {
      con = (HttpURLConnection) this.downloadURL.openConnection();
      setHeader(con);
      return con.getContentLength();
    } catch (IOException e) {
      log.info("用http协议从远程服务器获取文件长度时发生异常： " + e);
    }
    return 0;
  }

  private long[] getBreakpointPerThread() {
    long[] startPos = new long[numThread];
    File tempFileDir = new File(fileStorageDir);
    File[] files = tempFileDir.listFiles();
    for (int k = 0; k < files.length; k++) {
      String tempFileName = files[k].getName();
      if (tempFileName != null && files[k].length() > 0 && tempFileName.startsWith(fileName + "_")) {
        int fileIndex =
            Integer.parseInt(tempFileName.substring(tempFileName.lastIndexOf("_") + 1,
                tempFileName.lastIndexOf("_") + 2));
        startPos[fileIndex] = files[k].length();
      }
    }
    return startPos;
  }

  public boolean mergeFile(CountDownLatch latch) {
    boolean mergeFlag=false;
    try {
      latch.await();
      this.taskState = TaskState.MERGING;
      this.fileMergeTask =
          new FileMergeTask(this.fileSliceDownloadTask, this.numThread, this.fileStorageDir,
              this.fileName, this.taskId, this.statusError, this.mergeFlushSize, this.fileLength);
      Future<Boolean> future = TransferScheduler.getFixedExecutor().submit(fileMergeTask);
      try {
        mergeFlag = future.get();
      } catch (ExecutionException e) {
        log.info("taskId: " + taskId + "; 文件下载任务等待合并完成时线程中断：  " + e);
      }
    } catch (InterruptedException e) {
      log.info("taskId: " + taskId + "; 文件下载任务等待完成时线程中断：  " + e);
    }
    return mergeFlag;
  }
  public boolean verifyMD5(){
    boolean success=false;
    this.verifyMD5Task=new VerifyMD5Task(this.fileStorageDir+this.fileName,this.MD5 );
    Future<Boolean> future = TransferScheduler.getFixedExecutor().submit(verifyMD5Task);
    this.taskState = TaskState.VERIFING;
    try {
      success = future.get();
    } catch (InterruptedException e1) {
      log.info("taskId: " + taskId + "; 文件下载任务等待合并完成时线程中断：  " + e1);
    } catch (ExecutionException e1) {
      log.info("taskId: " + taskId + "; 文件下载任务等待合并完成时线程中断：  " + e1);
    }
    return success;
  }
  private void setHeader(URLConnection con) {
    con.setRequestProperty(
        "User-Agent",
        "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3");
    con.setRequestProperty("Accept-Language", "en-us,en;q=0.7,zh-cn;q=0.3");
    con.setRequestProperty("Accept-Encoding", "aa");
    con.setRequestProperty("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
    con.setRequestProperty("Keep-Alive", "300");
    con.setRequestProperty("Connection", "keep-alive");
    con.setRequestProperty("If-Modified-Since", "Fri, 02 Jan 2009 17:00:05 GMT");
    con.setRequestProperty("If-None-Match", "\"1261d8-4290-df64d224\"");
    con.setRequestProperty("Cache-Control", "max-age=0");
    con.setRequestProperty("Referer", "http://www.skycn.com/soft/14857.html");
  }

  public int getNumThread() {
    return numThread;
  }

  public void setNumThread(int numThread) {
    this.numThread = numThread;
  }

  public long getFileLengthPerThread() {
    return fileLengthPerThread;
  }

  public void setFileLengthPerThread(long fileLengthPerThread) {
    this.fileLengthPerThread = fileLengthPerThread;
  }

  private int computeProgress(){
    long count=this.fileLength;
    for(int i=0;i<this.fileSliceDownloadTask.length;++i){
      long startPos=this.fileSliceDownloadTask[i].getStartPos();
      long endPos=this.fileSliceDownloadTask[i].getEndPos();
      count-=(endPos-startPos);
    }
    
    int progress=(int) (count/this.fileLength);
    return progress;
  }
  public TaskStateResult getTaskStateResult() {
    this.taskStateResult.setTaskState(taskState);
    this.taskStateResult.setProgressPercentage("%"+computeProgress());
    return taskStateResult;
  }

  public boolean suspendTask() {
    if(this.taskState==TaskState.PROGRESSING){
      for(int i=0;i<this.fileSliceDownloadTask.length;++i)
        this.fileSliceDownloadTask[i].shutdownRequest();
    }
    else if(this.taskState==TaskState.VERIFING)
      this.verifyMD5Task.shutdownRequest();
    this.taskState=TaskState.SUSPENDING;
    log.info("文件传输任务id：" + this.getTaskId() + "被暂停！ ");
    return true;
  }

  public boolean resumeTask() {
    log.info("文件传输任务id：" + this.getTaskId() + "恢复！ ");
    work();
    return true;
  }

  public boolean deleteTask() {
    if(this.taskState==TaskState.PROGRESSING){
      for(int i=0;i<this.fileSliceDownloadTask.length;++i)
        this.fileSliceDownloadTask[i].shutdownRequest();
    }
    else if(this.taskState==TaskState.VERIFING)
      this.verifyMD5Task.shutdownRequest();
    clearDir(this.fileStorageDir);
    log.info("文件传输任务id：" + this.getTaskId() + "被删除！ ");
    return true;
  }

  private void clearDir(String fileDir) {
    File file = new File(fileDir);
    File tempFileDir = new File(fileDir);
    File[] files = tempFileDir.listFiles();
    for (int k = 0; k < files.length; k++) {
      files[k].delete();
    }
    tempFileDir.delete();
  }

}
